﻿#include "../../views/headers/ItemScene.h"
#include "../../beans/headers/BlockItemClass.h"
#include <QDebug>

ItemScene::ItemScene(int blockItemLastID,int lineItemLastID,QObject *parent):
    QGraphicsScene(parent),
    m_FlowBlockItemLastID(blockItemLastID),
    m_FlowLineItemLastID(lineItemLastID),
    m_IsMousePressed(false)
{

}

ItemScene::~ItemScene()
{
    qDeleteAll(m_LineItemClass_List);
    m_LineItemClass_List.clear();

    qDeleteAll(m_BlockItemClass_Map);
    m_BlockItemClass_Map.clear();
}

// 清除场景中的所有Item
void ItemScene::clear(void)
{
    QGraphicsScene::clear();

    qDeleteAll(m_BlockItemClass_Map);
    m_BlockItemClass_Map.clear();

    qDeleteAll(m_LineItemClass_List);
    m_LineItemClass_List.clear();
}

// 获取鼠标在场景中的实时坐标
QPointF ItemScene::getCursorPos(void)
{
    return m_CurrCusrsorPos;
}

// 将BlockItemClass插入Map中
void ItemScene::setBlockItemClassInsertIntoBlockMap(int blockID,BlockItemClass *blockItemClass)
{
    this->m_BlockItemClass_Map.insert(blockID,blockItemClass);
}

// 将BlockItemClass插入Map中
void ItemScene::setLineItemClassInsertIntoLineList(LineItemClass *lineItemClass)
{
    this->m_LineItemClass_List.append(lineItemClass);
}


// (辅助函数)场景中被选中的父界别Item
QList<QGraphicsItem *> ItemScene::fatherItemsBySelected(void)
{
    QList<QGraphicsItem *> fatherItemList;

    // 获取场景中所有被选中的Item（包括了父级别和子级别）
    QList<QGraphicsItem *> items = this->selectedItems();

    foreach (QGraphicsItem * item, items)
    {
        if(item->parentItem() == Q_NULLPTR)
            fatherItemList.append(item);
    }

    return fatherItemList;
}

//
void ItemScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    event->accept();
}

// 拖拽事件
void ItemScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    // 强制转换为子类 MimeData
    const MimeData *mimeData=static_cast<const MimeData*>(event->mimeData());

    // 若 "item" 是合格的自定义 MIME 类型，则把其图标和文本设置为该类型所关联的数据。
    // 本例未使用自定义 MIME 类型 "item" 的数据。
    if(mimeData->hasFormat("ItemTypeID"))
    {
        int itemTypeID = mimeData->getData();
        addFlowBlockItem(itemTypeID,event->scenePos());
    }

    event->accept();
}

void ItemScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QPointF cursorPos;
    QString itemClassStr;
    QGraphicsItem *cursorItem,*selectedItem;

    cursorPos = event->scenePos();

    // 设置实时鼠标坐标
    m_CurrCusrsorPos = cursorPos;

    cursorItem = this->itemAt(cursorPos,QTransform());

    // 此时Scene中只有一个被选中的Item
    if(!this->selectedItems().empty())
        selectedItem = this->selectedItems().first();
    else
        selectedItem = Q_NULLPTR;

    itemClassStr = (cursorItem != Q_NULLPTR ? itemClass2String(cursorItem->data(ITEM_CLASS).toInt()):"NULL");

    // 发送实时的鼠标坐标给到：FlowViewWindow::slot_currCursorPos(QPointF cursorPos)
    emit signal_CurrCursorPos(cursorPos);
    emit signal_CurrCursorPosItemClass(itemClassStr);

    emit signal_currCursorPosToFlowBlockItem(cursorPos);

    // 只要鼠标移动在BlockItem以及BlockItem的childItems都显示圆形端点DotItem
    if(cursorItem != Q_NULLPTR && cursorItem->data(ITEM_CLASS).toInt() != EnumType::ItemClass_TextItem)
    {

    }

    // 如果鼠标没有在任何Item上按压
    if(this->m_MousePressItem == Q_NULLPTR)
    {
        QGraphicsScene::mouseMoveEvent(event);
        return;
    }

    // 如果鼠标按压且是在SquareItem上，进行缩放操作
    if(this->m_IsMousePressed && this->m_MousePressItem->data(ITEM_CLASS) == EnumType::ItemClass_SquareItem)
    {
        SquareItem *squareItem = qgraphicsitem_cast<SquareItem *>(this->m_MousePressItem);
        zoomBlockItem(squareItem,cursorPos);

        // 如果鼠标按压且移动后悬浮在DotItem上（此时DotItem不是一开始按压的那个），进行画线操作
    }else if(this->m_IsMousePressed && this->m_MousePressItem->data(ITEM_CLASS) == EnumType::ItemClass_DotItem)
    {
        LineItemClass *lineItemClass = this->m_LineItemClass_List.back();

        if(cursorItem != Q_NULLPTR && cursorItem->data(ITEM_CLASS).toInt() == EnumType::ItemClass_DotItem)
            lineItemClass->setDesDotItem(qgraphicsitem_cast<DotItem *>(cursorItem));
        else
            lineItemClass->setDesDotItem(Q_NULLPTR);

        lineItemClass->drawManhattanLine(cursorPos);

        // 如果鼠标按压且是在FLowLineItem上，移动线操作
    }else if(this->m_IsMousePressed && this->m_MousePressItem->data(ITEM_CLASS) == EnumType::ItemClass_FLowLineItem)
    {
        FlowLineItem *lineItem = qgraphicsitem_cast<FlowLineItem *>(this->m_MousePressItem);

        qDebug()<<"lineItemID = "<<lineItem->data(ITEM_CLASS).toInt();

        // 移动线（同时与之链接的两条线也要更新点）
        this->moveLineItem(lineItem,cursorPos);

    }else if(this->m_IsMousePressed && !this->selectedItems().empty() && this->selectedItems().first()->data(ITEM_CLASS) == EnumType::ItemClass_FLowBlockItem)
    {
        QGraphicsScene::mouseMoveEvent(event);
        FlowBlockItem *blockItem = qgraphicsitem_cast<FlowBlockItem *>(this->selectedItems().first());
        this->moveBlockItem(blockItem);
    }else
    {
        QGraphicsScene::mouseMoveEvent(event);
    }

    // 根据BlockItem调节场景画布大小
    this->updateSceneRect(selectedItem);
}

// 鼠标按压事件
void ItemScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // 不管是鼠标的左键还是右键释放都要设为false
    this->m_IsMousePressed = true;

    // 获取当前鼠标所在的Item
    QGraphicsItem *pressItem = this->itemAt(event->scenePos(),QTransform());

    // 用于Item缩放的（鼠标按压时所在Item指针）
    this->m_MousePressItem = pressItem;

    // 如果鼠标处是DotItem，就需要画线
    if(this->m_MousePressItem != Q_NULLPTR && this->m_MousePressItem->data(ITEM_CLASS).toInt() == EnumType::ItemClass_DotItem)
    {
        DotItem *dotItem = qgraphicsitem_cast<DotItem *>(pressItem);
        LineItemClass *lineItem = new LineItemClass(dotItem,Q_NULLPTR,0);
        this->m_LineItemClass_List.append(lineItem);
    }

    QGraphicsScene::mousePressEvent(event);
}

// 鼠标释放
void ItemScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem *posItem = this->itemAt(event->scenePos(),QTransform());

    this->whetherDrawLine(posItem,m_LineItemClass_List);

    // 不管是鼠标的左键还是右键释放都要设为false
    this->m_IsMousePressed = false;

    // 用于Item缩放的Item指针（鼠标释放时所在Item指针设为空）
    this->m_MousePressItem = Q_NULLPTR;

    QGraphicsScene::mouseReleaseEvent(event);
}

// 鼠标按压事件
void ItemScene::keyPressEvent(QKeyEvent *event)
{
    QList<QGraphicsItem *> items = this->selectedItems();

    if(items.count() == 0)
        return;

    QGraphicsItem * item = items[0];

    FlowBlockItem *blockItem = qgraphicsitem_cast<FlowBlockItem *>(item);
    if(!(blockItem != Q_NULLPTR && item->parentItem() == Q_NULLPTR))
    {
        return;
    }

    // 向m_KeyCommand添加当前按下的键
    int command = event->key();
    m_KeyCommand.append(command);

    // 如果当前的链表中没有command键，就添加
    if(!m_KeyCommand.contains(command))
        m_KeyCommand.push_back(command);

    // 按下Control和方向键，BloItem移动一个像素位
    if(event->modifiers() == Qt::ControlModifier)
    {
        if(m_KeyCommand.contains(Qt::Key_Up))
        {
            blockItem->setPos(blockItem->scenePos().rx(),blockItem->scenePos().ry() - 1);
        }else if(m_KeyCommand.contains(Qt::Key_Down))
        {
            blockItem->setPos(blockItem->scenePos().rx(),blockItem->scenePos().ry() + 1);
        }else if(m_KeyCommand.contains(Qt::Key_Left))
        {
            blockItem->setPos(blockItem->scenePos().rx() - 1,blockItem->scenePos().ry() );
        }else if(m_KeyCommand.contains(Qt::Key_Right))
        {
            blockItem->setPos(blockItem->scenePos().rx() + 1,blockItem->scenePos().ry() );
        }
    }

    // 不要调用父部件默认的键盘事件，因为方向键会执行滚动条
}

//
void ItemScene::keyReleaseEvent(QKeyEvent *event)
{
    int command = event->key();
    m_KeyCommand.removeAll(command);
}

// 辅助函数（根据类别添加相应的item模块）
void ItemScene::addFlowBlockItem(int itemTypeID,QPointF cursor_Pos)
{
    int blockItemClassID = ++m_FlowBlockItemLastID;
    BlockItemClass *blockItemClass = new BlockItemClass(itemTypeID,blockItemClassID);

    FlowBlockItem *blockItem = blockItemClass->getBlockItem();

    blockItem->setPos(cursor_Pos.x() - blockItem->boundingRect().width() / 2 , cursor_Pos.y() - blockItem->boundingRect().height() / 2);

    this->addItem(blockItem);

    m_BlockItemClass_Map.insert(blockItemClassID,blockItemClass);
}

// 辅助函数（鼠标释放是否确定画线）
void ItemScene::whetherDrawLine(QGraphicsItem *cursorItem,QList<LineItemClass *> &lineItemClass_List)
{
    // LineItemID因为大于0说明是新加的线已经是有效线段，不能删除
    if(lineItemClass_List.size() == 0 )
        return;

    // 线对象的源端口、目标端口和当前鼠标所在的对象
    DotItem *souDotItem,*desDotItem;

    // 线对象
    LineItemClass *lineItemClass;
    lineItemClass = lineItemClass_List.back();

    souDotItem = lineItemClass->SouDotItem();

    // 检测如果lineItemClass的ID大于0，说明这是有效的线，不能被删除
    if(lineItemClass->LineItemID() > 0)
        return;

    if(cursorItem != Q_NULLPTR)
        desDotItem = (cursorItem->data(ITEM_CLASS).toInt() == EnumType::ItemClass_DotItem ? qgraphicsitem_cast<DotItem *>(cursorItem) : Q_NULLPTR);
    else
        desDotItem = Q_NULLPTR;

    if(desDotItem != Q_NULLPTR && desDotItem != souDotItem )
    {
        lineItemClass->setDesDotItem(desDotItem);

        lineItemClass->setLineItemID(m_FlowLineItemLastID++);

        // 曼哈顿路径避障
        lineItemClass->manhattanRoutePlan();

        // 曼哈顿路径归并
        lineItemClass->mergeRoute();
    }
    else
    {
        lineItemClass->clearLineItems();
        delete lineItemClass;

        lineItemClass = Q_NULLPTR;

        m_LineItemClass_List.removeLast();
    }
}

// 辅助函数（返回对角点矩形包涵和相交的所有FlowBlockItem指针列表）
QList<FlowBlockItem *> ItemScene::itemsAtRect(QPointF leftTopPoint,
                                              QPointF rightButtomPoint,
                                              Qt::ItemSelectionMode mode,
                                              Qt::SortOrder order,
                                              const QTransform &deviceTransform ) const
{
    QList<QGraphicsItem *> itemsList;
    QList<FlowBlockItem *> blockItemsList;
    qreal width = rightButtomPoint.rx() - leftTopPoint.rx();
    qreal height = rightButtomPoint.ry() - leftTopPoint.ry();
    itemsList = this->items(leftTopPoint.rx(),leftTopPoint.ry(),width,height,mode,order,deviceTransform);

    foreach (QGraphicsItem *tempItem, itemsList)
    {
        if(tempItem->type() == FlowBlockItem::Type)
        {
            FlowBlockItem *blockItem = qgraphicsitem_cast<FlowBlockItem *>(tempItem);
            blockItemsList.append(blockItem);
        }
    }

    return blockItemsList;
}

// 辅助函数（缩放BlockItem）
void ItemScene::zoomBlockItem(SquareItem *squareItem,QPointF cursorPos)
{
    FlowBlockItem *blockItem = qgraphicsitem_cast<FlowBlockItem *>(squareItem->parentItem());

    int blockItemClassID = blockItem->FlowBlockItemID();
    qDebug()<< "ID:"<<blockItemClassID;

    BlockItemClass *blockItemClass = m_BlockItemClass_Map.value(blockItemClassID);

    blockItemClass->zoomBlockItem(squareItem,cursorPos);

    moveBlockItem(blockItem);

    // 更新场景中该Item的区域，以便实时看到变化大小
    this->updateRectShow(blockItem);
}

// 更新该模块矩形区域的刷新显示
void ItemScene::updateRectShow(FlowBlockItem *blockItem)
{
    QPointF blcokItemScenePos,expandBlcokScenePos;
    qreal blockItemWidth,blockItemHeight,expandBlockItemWidth,expandBlockItemHeight;


    blcokItemScenePos = blockItem->scenePos();
    blockItemWidth = blockItem->boundingRect().width();
    blockItemHeight = blockItem->boundingRect().height();

    expandBlcokScenePos = blcokItemScenePos - QPointF(DotItem::DOTITEM_WIDTH / 2,DotItem::DOTITEM_WIDTH / 2);
    expandBlockItemWidth = blockItemWidth + DotItem::DOTITEM_WIDTH;
    expandBlockItemHeight = blockItemHeight + DotItem::DOTITEM_WIDTH;

    this->update(expandBlcokScenePos.rx(),expandBlcokScenePos.ry(),expandBlockItemWidth,expandBlockItemHeight);
}

QList<LineItemClass *> ItemScene::getLineItemClass_List() const
{
    return m_LineItemClass_List;
}

QMap<int, BlockItemClass *> ItemScene::getBlockItemClass_Map() const
{
    return m_BlockItemClass_Map;
}

// 辅助函数（根据当前的的父级别Item 实时变更整个Scene区域的大小。遗留问题：没有预留moreJudge的大小，还没找到问题）
void ItemScene::updateSceneRect(QGraphicsItem *fatherItem)
{
    // 如果是线就不操作更新场景大小
    if(fatherItem == Q_NULLPTR ||fatherItem->data(ITEM_CLASS).toInt() == EnumType::ItemClass_FLowLineItem)
        return;

    // 多一点距离
    qreal moreJudge = 10.0;

    QPointF topLeft = this->sceneRect().topLeft();
    QPointF bottomRight = this->sceneRect().bottomRight();

    qreal itemWidth = fatherItem->boundingRect().width();
    qreal itemHeight = fatherItem->boundingRect().height();
    QPointF itemPos = fatherItem->scenePos();

    qreal judegLeftTopX = itemPos.rx();
    qreal judegLeftTopY = itemPos.ry();
    qreal judegRightBottomX = itemPos.rx() + itemWidth;
    qreal judegRightBottomY = itemPos.ry() + itemHeight;

    if(judegLeftTopX < topLeft.rx())
        topLeft.setX(judegLeftTopX - moreJudge);

    if(judegLeftTopY < topLeft.ry())
        topLeft.setY(judegLeftTopY - moreJudge);

    if(judegRightBottomX > bottomRight.rx())
        bottomRight.setX(judegRightBottomX + moreJudge);

    if(judegRightBottomY > bottomRight.ry())
        bottomRight.setY(judegRightBottomY + moreJudge);

    this->setSceneRect(topLeft.rx(),topLeft.ry(),bottomRight.rx() - topLeft.rx(),bottomRight.ry() - topLeft.ry());
}

// 辅助函数（移动模块）
void ItemScene::moveBlockItem(FlowBlockItem *cursorBlockItem)
{
    BlockItemClass *blockItemClass;

    // BlockItem 四个端口的集合
    QList<DotItem *> blockDotItems;

    // 与BlockItem相连接的所有LineItemClass
    QList<LineItemClass *> blockItemLine_List;

    blockItemClass = m_BlockItemClass_Map.value(cursorBlockItem->FlowBlockItemID());
    blockDotItems = blockItemClass->getDotItem_List();

    // 遍历所有m_LineItemClass_List
    foreach (LineItemClass *lineItemClass, this->m_LineItemClass_List)
    {
        // 如果m_LineItemClass_List中的源端口或目标端口是该模块的端口就装入blockItemLine中
        if(blockDotItems.contains(lineItemClass->SouDotItem()) || blockDotItems.contains(lineItemClass->DesDotItem()))
        {
            lineItemClass->clearLineItems();
            lineItemClass->create5LineItems();
            blockItemLine_List.append(lineItemClass);
        }
    }

    foreach (LineItemClass *lineItemClass, blockItemLine_List)
    {
        // 线的源端口和目标端口
        DotItem *souDotItem,*desDotItem;

        // 目标端口中点坐标
        QPointF desDotItemCentralPos;

        souDotItem = lineItemClass->SouDotItem();
        desDotItem = lineItemClass->DesDotItem();
        desDotItemCentralPos = desDotItem->scenePos() + QPointF(DotItem::DOTITEM_WIDTH / 2,DotItem::DOTITEM_WIDTH / 2);

        lineItemClass->drawManhattanLine(desDotItemCentralPos);
        lineItemClass->manhattanRoutePlan();
        lineItemClass->mergeRoute();
    }
}

// 辅助函数（移动线）
void ItemScene::moveLineItem(FlowLineItem *lineItem,QPointF cursorPos)
{
    // 鼠标点击线的线元组的指针（线元组类封装了5个FLowLineItem）
    LineItemClass *lineItemClass;

    // 参数lineItem的两个端点
    QPointF lineItem_P1,lineItem_P2;

    // lineItem在LineItemClass的线元组中的下标值
    int lineItemIndex;

    lineItem_P1 = lineItem->line().p1();
    lineItem_P2 = lineItem->line().p2();

    // 通过遍历找到LineItemClass指针和FlowLineItem所在的下标值
    foreach (LineItemClass *line, this->m_LineItemClass_List)
    {
        QList<FlowLineItem *> flowItemList = line->LineItems_List();
        int index = flowItemList.indexOf(lineItem);

        if(index >= 0)
        {
            lineItemClass = line;
            lineItemIndex = index;
            break;
        }
    }

    // 如果线是首尾两条线，不让移动
    if(lineItemIndex <=0 || lineItemIndex == lineItemClass->LineItems_List().size()-1)
        return;

    // 如果线是垂直的，只能水平移动
    if(lineItem->line().p1().rx() == lineItem->line().p2().rx())
    {
        lineItem->setLine(cursorPos.rx(),lineItem_P1.ry(),cursorPos.rx(),lineItem_P2.ry());

        // 如果线是水平的，只能竖直移动
    }else
    {
        lineItem->setLine(lineItem_P1.rx(),cursorPos.ry(),lineItem_P2.rx(),cursorPos.ry());
    }

    lineItemClass->updateConnectiveLines(lineItem);
}

// 将场景转化为json格式
QJsonDocument ItemScene::toJsonDoc(void)
{
    QJsonDocument jsonDoc;
    QJsonObject rootJsonObj;

    QJsonArray blockItemJsonArray,lineItemJsonArray;

    // 将所有BlockItem模块转为Json
    auto iter = m_BlockItemClass_Map.begin();

    while (iter != m_BlockItemClass_Map.end())
    {
        BlockItemClass *blockItemClass = iter.value();

        blockItemJsonArray.append(QJsonValue( blockItemClass->toJsonObj() ));
        iter++;
    }

    rootJsonObj.insert("BlockItems",QJsonValue(blockItemJsonArray));

    // 将所有线模块LineItem转为Json
    for (int i = 0; i < m_LineItemClass_List.size(); ++i)
    {
        LineItemClass *lineItemClass = m_LineItemClass_List.at(i);
        lineItemJsonArray.append(QJsonValue(lineItemClass->toJsonObj()));
    }

    rootJsonObj.insert("LineItems",lineItemJsonArray);
    jsonDoc.setObject(rootJsonObj);

    return jsonDoc;
}

// 辅助函数（通过枚举ItemClass，返回对应的字符串）
QString ItemScene::itemClass2String(int itemClass)
{
    QString str;
    switch (itemClass) {
    case EnumType::ItemClass_FLowBlockItem:
        str = "FLowBlockItem";
        break;
    case EnumType::ItemClass_FLowLineItem:
        str = "FLowLineItem";
        break;
    case EnumType::ItemClass_DotItem:
        str = "DotItem";
        break;
    case EnumType::ItemClass_SquareItem:
        str = "SquareItem";
        break;
    case EnumType::ItemClass_TextItem:
        str = "TextItem";
        break;
    default:
        break;
    }

    return str;
}

